﻿using System;
using System.Collections.Generic;
using System.Linq;
using Lextm.SharpSnmpLib;
using Lextm.SharpSnmpLib.Messaging;
using SNMS.Manager.S_DAL.Entities;

namespace SNMS.Manager.Infrastructure
{
    // TODO:Don't forget changing public to internal after test
    public sealed class DeviceInfo
    {
        public DeviceInfo(SyncFlowItem item) {
            this.item = item;
        }

        private SyncFlowItem item;

        /// <summary>
        /// Sync basic mib info which device specfic.
        /// </summary>
        /// <returns>New Device which contains synced info.</returns>
        public Device BasicMib2InfoSync() {
            if (item.IPEndPoint == null) {
                return null;
            }

            var device = new Device();

            var system = SyncMib2System(device);
            var interfaces = SyncMib2Interfaces(device);
            var ip = SyncMib2Ip(device);

            device.Mib2_system = system;
            device.Mib2_interfaces = interfaces;
            device.Mib2_ip = ip;

            return device;
        }

        private Mib2_system SyncMib2System(Device device) {
            IList<Variable> variables = new List<Variable>();
            string oidPrefix = ".1.3.6.1.2.1.1";
            int count = Walk(new ObjectIdentifier(oidPrefix),variables);

            Mib2_system system = new Mib2_system() {
                sysDescr    = variables.OidValue(oidPrefix + ".1.0").ToString(),
                sysObjectID = variables.OidValue(oidPrefix + ".2.0").ToString(),
                sysUpTime   = variables.OidValue(oidPrefix + ".3.0").ToInt32(),
                sysContact  = variables.OidValue(oidPrefix + ".4.0").ToString(),
                sysName     = variables.OidValue(oidPrefix + ".5.0").ToString(),
                sysLocation = variables.OidValue(oidPrefix + ".6.0").ToString(),
                sysServices = variables.OidValue(oidPrefix + ".7.0").ToInt32()
            };
            system.Devices.Add(device);
            return system;
        }

        private Mib2_interfaces SyncMib2Interfaces(Device device)
        {
            IList<Variable> variables = new List<Variable>();
            string oidPrefix = ".1.3.6.1.2.1.2";
            int count = Walk(new ObjectIdentifier(oidPrefix), variables);

            int ifNumber = variables.OidValue(oidPrefix + ".1.0").ToInt32();
            var interfaces = new Mib2_interfaces() {
                ifNumber = ifNumber
            };
            for (int i = 0; i < ifNumber; i++) {
                string oidPrefixIfTable = oidPrefix + ".2.1";
                var entity = new Mib2_interfaces_ifEntry() { 
                    ifIndex             = i+0,
                    ifDescr             = variables.OidValue(oidPrefixIfTable+ ".2." + (i + 1)).ToString(),
                    ifType              = variables.OidValue(oidPrefixIfTable + ".3." + (i + 1)).ToInt32(),
                    ifMtu               = variables.OidValue(oidPrefixIfTable+".4."+(i+1)).ToInt32(),
                    ifSpeed             = variables.OidValue(oidPrefixIfTable+".5."+(i+1)).ToInt32(),
                    ifPhysAddress       = variables.OidValue(oidPrefixIfTable+".6."+(i+1)).ToString(),
                    ifAdminStatus       = variables.OidValue(oidPrefixIfTable+".7."+(i+1)).ToInt32(),
                    ifOperStatus        = variables.OidValue(oidPrefixIfTable+".8."+(i+1)).ToInt32(),
                    ifLastChange        = variables.OidValue(oidPrefixIfTable+".9."+(i+1)).ToInt32(),
                    ifInOctets          = variables.OidValue(oidPrefixIfTable+".10."+(i+1)).ToInt32(),
                    ifInUcastPkts       = variables.OidValue(oidPrefixIfTable+".11."+(i+1)).ToInt32(),
                    ifInNUcastPkts      = variables.OidValue(oidPrefixIfTable+".12."+(i+1)).ToInt32(),
                    ifInDiscards        = variables.OidValue(oidPrefixIfTable+".13."+(i+1)).ToInt32(),
                    ifInErrors          = variables.OidValue(oidPrefixIfTable+".14."+(i+1)).ToInt32(),
                    ifInUnknownProtos   = variables.OidValue(oidPrefixIfTable+".15."+(i+1)).ToInt32(),
                    ifOutOctets         = variables.OidValue(oidPrefixIfTable+".16."+(i+1)).ToInt32(),
                    ifOutUcastPkts      = variables.OidValue(oidPrefixIfTable+".17."+(i+1)).ToInt32(),
                    ifOutNUcastPkts     = variables.OidValue(oidPrefixIfTable+".18."+(i+1)).ToInt32(),
                    ifOutDiscards       = variables.OidValue(oidPrefixIfTable+".19."+(i+1)).ToInt32(),
                    ifOutErrors         = variables.OidValue(oidPrefixIfTable+".20."+(i+1)).ToInt32(),
                    ifOutQLen           = variables.OidValue(oidPrefixIfTable + ".21." + (i + 1)).ToInt32(),
                    ifSpecific          = variables.OidValue(oidPrefixIfTable+".22."+(i+1)).ToString()
                };
                entity.Mib2_interfaces.Add(interfaces);
                interfaces.Mib2_interfaces_ifEntry.Add(entity);
            }
            interfaces.Devices.Add(device);
            return interfaces;
        }

        private Mib2_ip SyncMib2Ip(Device device) {
            IList<Variable> variables = new List<Variable>();
            string oidPrefix = ".1.3.6.1.2.1.4";
            int count = Walk(new ObjectIdentifier(oidPrefix), variables);
            int rowCount = 0;
            #region ip
            var ip = new Mib2_ip() { 
                ipForwarding        = variables.OidValue(oidPrefix + ".1.0").ToInt32(),
                ipDefaultTTL        = variables.OidValue(oidPrefix + ".2.0").ToInt32(),
                ipInReceives        = variables.OidValue(oidPrefix + ".3.0").ToInt32(),
                ipInHdrErrors       = variables.OidValue(oidPrefix + ".4.0").ToInt32(),
                ipInAddrErrors      = variables.OidValue(oidPrefix + ".5.0").ToInt32(),
                ipForwDatagrams     = variables.OidValue(oidPrefix + ".6.0").ToInt32(),
                ipInUnknownProtos   = variables.OidValue(oidPrefix + ".7.0").ToInt32(),
                ipInDiscards        = variables.OidValue(oidPrefix + ".8.0").ToInt32(),
                ipInDelivers        = variables.OidValue(oidPrefix + ".9.0").ToInt32(),
                ipOutRequests       = variables.OidValue(oidPrefix + ".10.0").ToInt32(),
                ipOutDiscards       = variables.OidValue(oidPrefix + ".11.0").ToInt32(),
                ipOutNoRoutes       = variables.OidValue(oidPrefix + ".12.0").ToInt32(),
                ipReasmTimeout      = variables.OidValue(oidPrefix + ".13.0").ToInt32(),
                ipReasmReqds        = variables.OidValue(oidPrefix + ".14.0").ToInt32(),
                ipReasmOKs          = variables.OidValue(oidPrefix + ".15.0").ToInt32(),
                ipReasmFails        = variables.OidValue(oidPrefix + ".16.0").ToInt32(),
                ipFragOKs           = variables.OidValue(oidPrefix + ".17.0").ToInt32(),
                ipFragFails         = variables.OidValue(oidPrefix + ".18.0").ToInt32(),
                ipFragCreates       = variables.OidValue(oidPrefix + ".19.0").ToInt32(),
                ipRoutingDiscards   = variables.OidValue(oidPrefix + ".23.0").ToInt32()
            };
            #endregion

            #region ipAddrTable
            string oidPrefixIpAddrTable = oidPrefix + ".20.1";
            var ipAddrTable = variables.Where(_ => _.Id.ToString().StartsWith(oidPrefixIpAddrTable))
                                        .OrderBy(_ => _.Id).ToList();
            rowCount = ipAddrTable.Count() / 5;
            for (int i = 0; i < rowCount; i++) {
                var ipAddrEntry = new Mib2_ip_ipAddrEntry() {
                    ipAdEntAddr             = ipAddrTable[0 * rowCount + i].Data.ToString(),
                    ipAdEntIndex            = ipAddrTable[1 * rowCount + i].Data.ToInt32(),
                    ipAdEntNetMask          = ipAddrTable[2 * rowCount + i].Data.ToString(),
                    ipAdEntBcastAddr        = ipAddrTable[3 * rowCount + i].Data.ToInt32(),
                    ipAdEntReasmMaxSize     = ipAddrTable[4 * rowCount + i].Data.ToInt32()
                };
                ipAddrEntry.Mib2_ip.Add(ip);
                ip.Mib2_ip_ipAddrEntry.Add(ipAddrEntry);
            }
            #endregion

            #region ipRouteTable
            string oidPrefixIpRouteTable = oidPrefix + "21.1";
            var ipRouteTable = variables.Where(_ => _.Id.ToString().StartsWith(oidPrefixIpRouteTable))
                                        .OrderBy(_ => _.Id).ToList();
            rowCount = ipRouteTable.Count() / 13;
            for (int i = 0; i < rowCount; i++) {
                var ipRouteEntry = new Mib2_ip_ipRouteEntry() { 
                    ipRouteDest             = ipRouteTable[0 * rowCount + i].Data.ToString(),
                    ipRouteIfIndex          = ipRouteTable[1 * rowCount + i].Data.ToInt32(),
                    ipRouteMetric1          = ipRouteTable[2 * rowCount + i].Data.ToInt32(),
                    ipRouteMetric2          = ipRouteTable[3 * rowCount + i].Data.ToInt32(),
                    ipRouteMetric3          = ipRouteTable[4 * rowCount + i].Data.ToInt32(),
                    ipRouteMetric4          = ipRouteTable[5 * rowCount + i].Data.ToInt32(),
                    ipRouteNextHop          = ipRouteTable[6 * rowCount + i].Data.ToString(),
                    ipRouteType             = ipRouteTable[7 * rowCount + i].Data.ToInt32(),
                    ipRouteProto            = ipRouteTable[8 * rowCount + i].Data.ToInt32(),
                    ipRouteAge              = ipRouteTable[9 * rowCount + i].Data.ToInt32(),
                    ipRouteMask             = ipRouteTable[10 * rowCount + i].Data.ToString(),
                    ipRouteMetric5          = ipRouteTable[11 * rowCount + i].Data.ToInt32(),
                    ipRouteInfo             = ipRouteTable[12 * rowCount + i].Data.ToString()
                };
                ipRouteEntry.Mib2_ip.Add(ip);
                ip.Mib2_ip_ipRouteEntry.Add(ipRouteEntry);
            }
            #endregion

            #region ipNetToMediaTable
            string oidPrefixIpNetToMediaTable = oidPrefix + "22.1";
            var ipNetToMediaTable = variables.Where(_ => _.Id.ToString().StartsWith(oidPrefixIpNetToMediaTable))
                                        .OrderBy(_ => _.Id).ToList();
            rowCount = ipNetToMediaTable.Count() / 4;
            for (int i = 0; i < rowCount; i++) {
                var ipNetToMediaEntry = new Mib2_ip_ipNetToMediaEntry() {
                    ipNetToMediaIfIndex     = ipNetToMediaTable[0 * rowCount + i].Data.ToInt32(),
                    ipNetToMediaPhysAddress = ipNetToMediaTable[1 * rowCount + i].Data.ToString(),
                    ipNetToMediaNetAddress  = ipNetToMediaTable[2 * rowCount + i].Data.ToString(),
                    ipNetToMediaType        = ipNetToMediaTable[3 * rowCount + i].Data.ToString()
                };
                ipNetToMediaEntry.Mib2_ip.Add(ip);
                ip.Mib2_ip_ipNetToMediaEntry.Add(ipNetToMediaEntry);
            }
            #endregion

            ip.Devices.Add(device);
            return ip;
        }

        private int Walk(ObjectIdentifier oid, IList<Variable> results) {
            if (item.Version == VersionCode.V1) {
                return Messenger.Walk(item.Version, item.IPEndPoint, item.Community, oid, results, 10000, WalkMode.WithinSubtree);
            } 
            else if (item.Version == VersionCode.V2) {
                return Messenger.BulkWalk(item.Version, item.IPEndPoint, item.Community, oid, results, 10000, 10, WalkMode.WithinSubtree, null, null);
            } 
            else {
                throw new NotImplementedException();//Snmp v3
            }
        }
    }
}
